﻿#region header

//  ***************************************************************************
// *                                LICENSE
//  ***************************************************************************
//  * "Haplous" .NET Simple 2D Game Framework
//  * 
//  * Copyright © Jonathan Evans 2014-2014
//  * 
//  * This program is free software; you can redistribute it and/or
//  * modify it under the terms of the GNU General Public License
//  * as published by the Free Software Foundation; either version 2
//  * of the License, or (at your option) any later version.
//  * 
//  * This program is distributed in the hope that it will be useful,
//  * but WITHOUT ANY WARRANTY; without even the implied warranty of
//  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  * GNU General Public License for more details.
//  * 
//  * You should have received a copy of the GNU General Public License
//  * along with this program; if not, write to the Free Software
//  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//  * 
//  ***************************************************************************
//  * 
//  * Contact: opencollar@googlemail.com
//  * 
//  * Website: http://haplous.codeplex.com/
//  *
//  * NuGet:   http://www.nuget.org/packages/haplous/
//  *
//  * File:    Haplous/Haplous/ImageToImageSourceConverter.cs
//  *
//  ***************************************************************************

#endregion

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Imaging;

using Haplous.Properties;

using JetBrains.Annotations;

namespace Haplous.Presentation.Converters
{
    /// <summary>
    ///     A converter that translates between <see cref="System.Drawing.Image" /> and
    ///     <see cref="System.Windows.Media.ImageSource" /> formats.
    /// </summary>
    [ValueConversion(typeof(Image), typeof(ImageSource))]
    public class ImageToImageSourceConverter : IValueConverter
    {
        #region IValueConverter Members

        /// <summary>
        ///     A cache of images created.
        /// </summary>
        /// <remarks>Concurrent access is controlled by the <see cref="_cacheLock" /> field.</remarks>
        [NotNull]
        private static readonly Dictionary<object, BitmapImage> _cache = new Dictionary<object, BitmapImage>();

        /// <summary>
        ///     A lock used to control concurrent access to the <see cref="_cache" /> field.
        /// </summary>
        [NotNull]
        private static readonly ReaderWriterLockSlim _cacheLock = new ReaderWriterLockSlim();

        /// <summary>
        ///     Converts a value.
        /// </summary>
        /// <returns>
        ///     A converted value. If the method returns null, the valid null value is used.
        /// </returns>
        /// <param name="value">The value produced by the binding source.</param>
        /// <param name="targetType">The type of the binding target property.</param>
        /// <param name="parameter">The converter parameter to use.</param>
        /// <param name="culture">The culture to use in the converter.</param>
        [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if((targetType != typeof(ImageSource)) && (targetType != typeof(BitmapImage)))
                throw new ArgumentOutOfRangeException("targetType", Resources.Error_ImageTypeNotSupported);

            var image = value as Image;
            if(image == null)
                return null;

            _cacheLock.EnterUpgradeableReadLock();
            try
            {
                BitmapImage source;

                if(_cache.TryGetValue(value, out source))
                    return source;

                _cacheLock.EnterWriteLock();
                try
                {
                    source = new BitmapImage();
                    source.BeginInit();
                    var stream = new MemoryStream();

                    image.Save(stream, ImageFormat.Png);
                    stream.Seek(0, SeekOrigin.Begin);
                    source.StreamSource = stream;
                    source.EndInit();
                    source.Freeze();

                    _cache.Add(value, source);

                    return source;
                }
                finally
                {
                    _cacheLock.ExitWriteLock();
                }
            }
            finally
            {
                _cacheLock.ExitUpgradeableReadLock();
            }
        }

        /// <summary>
        ///     Converts a value.
        /// </summary>
        /// <returns>
        ///     A converted value. If the method returns null, the valid null value is used.
        /// </returns>
        /// <param name="value">The value that is produced by the binding target.</param>
        /// <param name="targetType">The type to convert to.</param>
        /// <param name="parameter">The converter parameter to use.</param>
        /// <param name="culture">The culture to use in the converter.</param>
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var source = value as ImageSource;
            if(source == null)
                return null;

            throw new NotImplementedException();
        }

        #endregion
    }
}